classdef Blazer
    properties
        Name
        Eqtn
        IxOccur
        IxEqtn
        IxEndg
        IxCanBeEndg
        
        EqtnBlk
        NameBlk
        IsSingular
    end
    
    
    
    
    methods
        function this = endogenize(this, lsEndg)
            testFunc = @(this, pos) this.IxCanBeEndg(pos);
            this = swap(this, lsEndg, true, testFunc, exception.Blazer.CANNOT_ENDOGENIZE);
        end
        
        
        
        
        function this = exogenize(this, lsExg)
            testFunc = @(this, pos) this.IxEndg(pos);
            this = swap(this, lsExg, false, testFunc, exception.Blazer.CANNOT_EXOGENIZE);
        end
        
        
        
        
        function this = swap(this, list, setEndgTo, testFunc, exception)
            if ischar(list)
                list = regexp(list, '\w+', 'match');
            end
            if isempty(list)
                return
            end
            list = unique(list);
            nList = length(list);
            ixValid = true(1,nList);
            for i = 1 : nList
                name = list{i};
                pos = find( strcmp(this.Name,name), 1 );
                if isempty(pos) || ~testFunc(this, pos)
                    ixValid(i) = false;
                    continue
                end
                this.IxEndg(pos) = setEndgTo;
            end
            if any(~ixValid)
                throw( exception, list{~ixValid} );
            end
        end
        
        
        
        
        function this = createBlocks(this, isBlocks)
            nEqtn = sum(this.IxEqtn);
            nEndg = sum(this.IxEndg);
            if nEqtn~=nEndg
                throw( exception.Blazer.NUMBER_EQUATIONS_ENDOGENIZED, ...
                    nEqtn, nEndg ); %#ok<GTARG>
            end
            eqtnId = find(this.IxEqtn);
            nameId = find(this.IxEndg);
            if ~isBlocks
                this.EqtnBlk = { eqtnId };
                this.NameBlk = { nameId };
                return
            end
            [ordIxOccur, ordEqtnId, ordNameId] = Blazer.reorder( ...
                this.IxOccur(this.IxEqtn, this.IxEndg), ...
                eqtnId, nameId ...
                );
            this.IsSingular = sprank(ordIxOccur)<min(size(ordIxOccur));
            [this.EqtnBlk, this.NameBlk] = Blazer.getBlocks( ...
                ordIxOccur, ordEqtnId, ordNameId ...
                );
        end
        
        
        
        
        function [eqtnBlkHuman, nameBlkHuman] = getHuman(this)
            eqtnBlkHuman = cell(size(this.EqtnBlk));
            nameBlkHuman = cell(size(this.NameBlk));
            for i = 1 : length(this.EqtnBlk)
                eqtnBlkHuman{i} = this.Eqtn( this.EqtnBlk{i} );
            end
            for i = 1 : length(this.NameBlk)
                nameBlkHuman{i} = this.Name( this.NameBlk{i} );
            end
        end
    end
    
    
    
    
    methods (Static)
        function [ixOccur, eqtnId, nameId] = reorder(ixOccur, eqtnId, nameId)
            if isempty(ixOccur)
                return
            end
            
            c1 = colamd(ixOccur);
            ixOccur = ixOccur(:,c1);
            nameId = nameId(c1);
            
            r1 = colamd(ixOccur.');
            ixOccur = ixOccur(r1,:);
            eqtnId = eqtnId(r1);
            
            [r2,c2] = dmperm(ixOccur);
            ixOccur = ixOccur(r2,c2);
            nameId = nameId(c2);
            eqtnId = eqtnId(r2);
        end
        
        
        
        
        function [eqtnBlk, nameBlk] = getBlocks(IxOccur, eqtnId, nameId)
            n = size(IxOccur,1);
            eqtnBlk = cell(1,0);
            nameBlk = cell(1,0);
            currEqtnBlk = [ ];
            currNameBlk = [ ];
            for i = n : -1 : 1
                currNameBlk(end+1) = nameId(i); %#ok<AGROW>
                currEqtnBlk(end+1) = eqtnId(i); %#ok<AGROW>
                if ~any( any( IxOccur(i:end, 1:i-1) ) )
                    nameBlk{end+1} = currNameBlk; %#ok<AGROW>
                    eqtnBlk{end+1} = currEqtnBlk; %#ok<AGROW>
                    currNameBlk = [ ];
                    currEqtnBlk = [ ];
                end
            end
        end
    end
end